package trackexplorer.views;

import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.LinkedList;
import java.util.Map;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.preferences.ConfigurationScope;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;
import org.osgi.service.prefs.BackingStoreException;
import org.osgi.service.prefs.Preferences;

import trackexplorer.Calibration;
import trackexplorer.editors.MapInput;
import trackexplorer.editors.MapViewer;

public class TrackView extends ViewPart{
    public static final String  ID = "trackexplorer.views.TrackView";
    private CheckboxTableViewer viewer;
    private Action              openTrackAction;
    private Action              doubleClickAction;
    private Preferences         prefs;
    private CheckStateListener  checkListener;
    private Action              removeTrackAction;
    private Action              openAllTracksAction;
    private Action              openTrackMapAction;
    private Action              editDirsAction;

    class ViewContentProvider implements IStructuredContentProvider{
        public void inputChanged(Viewer v, Object oldInput, Object newInput){}

        public void dispose(){}

        public Object[] getElements(Object parent){
            LinkedList<String> ret = new LinkedList<String>();
            try{
                Preferences node = prefs.node("tracks");
                for(String f: node.keys()){
                    if(!new File(f).exists()){
                        node.remove(f);
                    }else{
                        ret.add(f);
                    }
                }
                prefs.flush();
            }catch(BackingStoreException e){
                e.printStackTrace();
            }
            return ret.toArray();
        }
    }
    class ViewLabelProvider extends LabelProvider implements
        ITableLabelProvider{
        public String getColumnText(Object obj, int index){
            return getText(obj);
        }

        public Image getColumnImage(Object obj, int index){
            return getImage(obj);
        }

        @Override
        public Image getImage(Object obj){
            return PlatformUI.getWorkbench().getSharedImages().getImage(
                ISharedImages.IMG_OBJ_ELEMENT);
        }
    }

    public TrackView(){
        prefs = new ConfigurationScope().getNode(MapViewer.ID);
    }

    public void checkTracks(Object[] elements){
        viewer.setCheckedElements(elements);
    }
    class CheckStateListener implements ICheckStateListener{

        public void checkStateChanged(CheckStateChangedEvent event){
            Object element = event.getElement();
            String el = element.toString();
            IEditorPart activeEditor = PlatformUI.getWorkbench()
                .getActiveWorkbenchWindow().getActivePage().getActiveEditor();
            if(activeEditor instanceof MapViewer){
                MapViewer editor = (MapViewer)activeEditor;
                if(event.getChecked()){
                    if(!editor.addTrack(new File(el))){
                        viewer.setChecked(element, false);
                    }
                }else{
                    editor.removeTrack(el);
                }
            }else{
                viewer.setChecked(element, false);
            }
        }
    };

    @Override
    public void createPartControl(Composite parent){
        viewer = CheckboxTableViewer.newCheckList(parent, SWT.H_SCROLL
            | SWT.V_SCROLL);
        viewer.setContentProvider(new ViewContentProvider());
        viewer.setSorter(new ViewerSorter());
        viewer.setInput(getViewSite());
        checkListener = new CheckStateListener();
        viewer.addCheckStateListener(checkListener);
        viewer.addDropSupport(DND.DROP_COPY | DND.DROP_MOVE,
            new Transfer[]{FileTransfer.getInstance()},
            new DropTargetAdapter(){

                @Override
                public void drop(DropTargetEvent event){
                    Preferences tracks = prefs.node("tracks");
                    for(String s: (String[])event.data){
                        File file = new File(s);
                        if(!file.getName().toLowerCase().endsWith(".gpx")){
                            continue;
                        }
                        tracks.put(file.getAbsolutePath(), file.getName());
                    }
                    try{
                        prefs.flush();
                    }catch(BackingStoreException e){
                        e.printStackTrace();
                    }
                    viewer.refresh();
                }
            });
        makeActions(parent);
        hookContextMenu();
        hookDoubleClickAction();
        contributeToActionBars();
    }

    private void makeActions(final Composite parent){
        openTrackAction = new Action(){
            @Override
            public void run(){
                FileDialog dialog = new FileDialog(parent.getShell(), SWT.OPEN);
                dialog.setFilterNames(new String[]{"Track Files",
                    "All Files (*.*)"});
                dialog.setFilterExtensions(new String[]{"*.gpx", "*.*"});
                dialog.setFilterPath(prefs.get("trackdir", "/"));
                String open = dialog.open();
                if(open == null){
                    return;
                }
                File file = new File(open);
                prefs.put("trackdir", file.getParent());
                Preferences tracks = prefs.node("tracks");
                String el = file.getAbsolutePath();
                tracks.put(el, file.getName());
                try{
                    prefs.flush();
                }catch(BackingStoreException e){
                    e.printStackTrace();
                }
                viewer.refresh();
                viewer.setChecked(el, true);
                checkListener.checkStateChanged(new CheckStateChangedEvent(
                    viewer, el, true));
            }
        };
        openTrackAction.setText("Open track");
        openTrackAction.setToolTipText("Open track");
        openTrackAction.setImageDescriptor(PlatformUI.getWorkbench()
            .getSharedImages().getImageDescriptor(ISharedImages.IMG_OBJ_FILE));
        doubleClickAction = new Action(){
            @Override
            public void run(){
                Object el = ((IStructuredSelection)viewer.getSelection())
                    .getFirstElement();
                boolean state = !viewer.getChecked(el);
                viewer.setChecked(el, state);
                checkListener.checkStateChanged(new CheckStateChangedEvent(
                    viewer, el, state));
            }
        };
        removeTrackAction = new Action(){
            @Override
            public void run(){
                Object el = ((IStructuredSelection)viewer.getSelection())
                    .getFirstElement();
                viewer.remove(el);
                Preferences tracks = prefs.node("tracks");
                tracks.remove(el.toString());
                try{
                    prefs.flush();
                }catch(BackingStoreException e){
                    e.printStackTrace();
                }
            }
        };
        removeTrackAction.setText("Remove track");
        removeTrackAction.setToolTipText("Remove track from list");
        removeTrackAction.setImageDescriptor(PlatformUI.getWorkbench()
            .getSharedImages()
            .getImageDescriptor(ISharedImages.IMG_TOOL_DELETE));
        openAllTracksAction = new Action(){
            @Override
            public void run(){
                DirectoryDialog dialog = new DirectoryDialog(parent.getShell(),
                    SWT.OPEN);
                dialog.setFilterPath(prefs.get("tracksdir", "/"));
                final String open = dialog.open();
                if(open == null){
                    return;
                }
                IRunnableWithProgress op = new IRunnableWithProgress(){
                    public void run(IProgressMonitor monitor)
                        throws InvocationTargetException, InterruptedException{
                        File file = new File(open);
                        prefs.put("tracksdir", file.getParent());
                        findTrackFiles(file);
                    }
                };
                try{
                    new ProgressMonitorDialog(PlatformUI.getWorkbench()
                        .getActiveWorkbenchWindow().getShell()).run(true, true,
                        op);
                }catch(Exception e){
                    e.printStackTrace();
                }
                try{
                    prefs.flush();
                }catch(BackingStoreException e){
                    e.printStackTrace();
                }
                viewer.refresh();
            }
        };
        openAllTracksAction.setText("Open all tracks");
        openAllTracksAction.setToolTipText("Open all tracks in directory");
        openAllTracksAction
            .setImageDescriptor(PlatformUI.getWorkbench().getSharedImages()
                .getImageDescriptor(ISharedImages.IMG_OBJ_FOLDER));

        editDirsAction = new Action(){
            @Override
            public void run(){
                Object el = ((IStructuredSelection)viewer.getSelection())
                    .getFirstElement();
                String tr = el.toString();
                try{
                    EditDirsView view = (EditDirsView)PlatformUI.getWorkbench()
                        .getActiveWorkbenchWindow().getActivePage().showView(
                            EditDirsView.ID);
                    view.setFile(tr);
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        };
        editDirsAction.setText("Edit directions");
        editDirsAction.setToolTipText("Edit directions");
        editDirsAction.setImageDescriptor(PlatformUI.getWorkbench()
            .getSharedImages()
            .getImageDescriptor(ISharedImages.IMG_OBJ_ELEMENT));

        openTrackMapAction = new Action(){
            @Override
            public void run(){
                IRunnableWithProgress op = new IRunnableWithProgress(){
                    public void run(IProgressMonitor monitor)
                        throws InvocationTargetException, InterruptedException{
                        Object el = ((IStructuredSelection)viewer
                            .getSelection()).getFirstElement();
                        findMap(el);
                        viewer.setChecked(el, true);
                        checkListener
                            .checkStateChanged(new CheckStateChangedEvent(
                                viewer, el, true));
                    }
                };
                try{
                    new ProgressMonitorDialog(PlatformUI.getWorkbench()
                        .getActiveWorkbenchWindow().getShell()).run(true, true,
                        op);
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        };
        openTrackMapAction.setText("Open map with track");
        openTrackMapAction.setToolTipText("Open map with track");
        openTrackMapAction.setImageDescriptor(PlatformUI.getWorkbench()
            .getSharedImages()
            .getImageDescriptor(ISharedImages.IMG_OBJ_ELEMENT));
    }

    private void hookDoubleClickAction(){
        viewer.addDoubleClickListener(new IDoubleClickListener(){
            public void doubleClick(DoubleClickEvent event){
                doubleClickAction.run();
            }
        });
    }

    private void hookContextMenu(){
        MenuManager menuMgr = new MenuManager("#PopupMenu");
        menuMgr.setRemoveAllWhenShown(true);
        menuMgr.addMenuListener(new IMenuListener(){
            public void menuAboutToShow(IMenuManager manager){
                TrackView.this.fillContextMenu(manager);
            }
        });
        Menu menu = menuMgr.createContextMenu(viewer.getControl());
        viewer.getControl().setMenu(menu);
        getSite().registerContextMenu(menuMgr, viewer);
    }

    private void contributeToActionBars(){
        IActionBars bars = getViewSite().getActionBars();
        fillLocalToolBar(bars.getToolBarManager());
    }

    private void fillContextMenu(IMenuManager manager){
        manager.add(openTrackMapAction);
        manager.add(editDirsAction);
        manager.add(removeTrackAction);
    }

    private void fillLocalToolBar(IToolBarManager manager){
        manager.add(openTrackAction);
        manager.add(openAllTracksAction);
    }

    @Override
    public void setFocus(){
        viewer.getControl().setFocus();
    }

    private void findTrackFiles(File dir){
        File[] files = dir.listFiles();
        for(File file: files){
            if(file.getName().toLowerCase().endsWith(".gpx")){
                prefs.node("tracks")
                    .put(file.getAbsolutePath(), file.getName());
            }
            if(file.isDirectory()){
                findTrackFiles(file);
            }
        }
    }

    private void findMap(Object el){
        String tr = el.toString();

        Preferences node = prefs.node("maps");
        try{
            for(String f: node.keys()){
                Calibration calibration = new Calibration();
                calibration.readMap(new FileInputStream(f));
                Map<int[], String> gpx = calibration.readGpx(new File(tr));
                int[] r = new LinkedList<int[]>(gpx.keySet()).getFirst();
                Rectangle rec = new Rectangle(0, 0, calibration.getSize().x,
                    calibration.getSize().y);
                if(rec.contains(r[0], r[1])){
                    IWorkbenchPage page = PlatformUI.getWorkbench()
                        .getActiveWorkbenchWindow().getActivePage();

                    MapViewer mv = (MapViewer)page.openEditor(new MapInput(
                        new File(f)), MapViewer.ID);
                    mv.showPoint(r[0], r[1]);
                    return;
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}
